<?php
/*
    Dill is a tool for creating websites with PHP.
    Copyright (C) 2014  Jannik Haberbosch

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Functions
function create_page( $db, $chosen_pagename, $selected_template,
	$template_containers, $sort_id, &$error_msg, $parent_id = NULL )
{
	/* Calls two functions.  The first function creates a new record in the
	database table 'page', which represents a single page of a website project.
	The second function creates n records in the database table 'container'.

	Parameters		$db
					SQLite3 Object
					Represents an established connecton to an SQLite3 database.

					$chosen_pagename
					string
					The name of the page.

					$selected_template
					string
					The name of the selected template.

					$template_containers
					array
					An array that contains strings.  Each of these strings de-
					fines a name for a container to create.

					$sort_id
					integer
					Defines the position of a page in a branch of the website
					structure.

					&$error_msg
					string
					An empty string that will be replaced by an appropriate
					error message, should an error occur.

					$parent_id
					integer or NULL.
					The 'id' value (db column) of the record that will be the
					parent of the page to create.

	Return values	TRUE/FALSE
					boolean
					FALSE is returned on failure; TRUE on success.

	Please read through the body of the function in order to know how it works.

	*/
	// Try to create a new record in the database table 'page'.
	if( !db_create_page(
		$db,
		$chosen_pagename,
		$selected_template,
		$insert_id,
		$sort_id,
		$parent_id
	) )
	{
		// Error:  The record could not be created.
		/* TODO:  Make the error message a constant to allow for multiple
		languages. */
		$error_msg = "Page could not be created.";
		return FALSE;
	}
	// Try to create n records in the database table 'container'.
	if( !db_create_containers(
		$db,
		$template_containers,
		$insert_id
	) )
	{
		// Error:  The records could not be created.
		/* TODO:  Make the error message a constant to allow for multiple
		langauges. */
		$error_msg = "Containers could not be created.";
		return FALSE;
	}
	return TRUE;
}


function generate_pages( $website_structure, $root_dir, $abs_template_path, $db )
{
	/* Generates the website directory structure and the .HTML pages of course.

	Throws an Exception if an error has occured.

	Parameters		$website_structure
					array
					An array that contains a representation of the structure of
					the website.  Each item is an array that consists of several
					attributes, each of which are defined in the database.
					So, essentially, all items are just a mere representation of
					their respective values on the database table 'page'.

					$root_dir
					string
					The absolute path to the "out/pages" directory of a website
					project.

					$abs_template_path
					string
					The absolute path to the "in/templates" directory of a website
					project.

					$db
					SQLite3 object.
					Represents an established connection to an SQLIte3 database.

	Return values	Void

	For a detailed idea of how the function works, please read through the body
	of this function.

	*/
	// Each page in the website structure will be created.
	foreach( $website_structure as $page_data )
	{
		// The template which the current page uses.
		$template = file_get_contents(
			makepath(
				$abs_template_path,
				$page_data["template"]
			)
		);
		if( $template === FALSE )
		{
			// Contents of the template could not be read.
			throw new Exception(
				sprintf(
					ERROR_READ_FILE_CONTENTS,
					$page_data["template"]
				)
			);
		}

		// The containers of the current page.
		$page_containers = db_container_get_by_page_id2(
			$db,
			$page_data["id"]
		);
		if( $page_containers == FALSE )
		{
			// Page containers could not be retrieved.
			throw new Exception(
				sprintf(
					ERROR_DB_RETRIEVE_PAGE_CONTAINERS,
					$page_data["name"]
				)
			);
		}

		/* Find and execute any functions, that are contained in a page container.
		and save the result. */
		foreach( $page_containers as &$pc )
		{
			$pc["content"] = find_and_execute_functions(
				$pc["content"]
			);
		}

				
		/* Find and execute any functions, that are contained in the template
		and save the results. */
		$template = find_and_execute_functions(
			$template
		);
		
		// Replace page containers of the template by their respective values.
		$search = array();
		$replace = array();

		foreach( $page_containers as &$pc )
		{
			$search[] = $pc["name"];
			$replace[] = $pc["content"];
		}
		
		/* Generate the final HTML code with all containers being replaced with
		their respective values. */
		$final_html_code = str_replace(
			$search,
			$replace,
			$template
		);
		
		// Create the directory and the .html file.
		$res = mkdir(
			makepath(
				$root_dir,
				$page_data["name"]
			),
			0777,
			TRUE
		);
		if( $res == FALSE )
		{
			// The directory could not be created.
			throw new Exception(
				sprintf(
					ERROR_CREATE_DIRECTORY,
					$page_data["name"]
				)
			);
		}

		// Write the contents into the "index.html" file.	
		$res = file_put_contents(
			makepath(
				$root_dir,
				$page_data["name"],
				"index.html"
			),
			$final_html_code
		);
		if( $res === FALSE )
		{
			// Contents could not be written.
			throw new Exception(
				sprintf(
					ERROR_CREATE_FILE,
					"index.html"
				)
			);
		}
		
		// Check whether a branch exists and move into move that branch.
		if( is_array( $page_data["children"] ) && $page_data["children"] >= 1 )
		{
			try
			{
				generate_pages(
					$page_data["children"],
					makepath(
						$root_dir,
						$page_data["name"]
					),
					$abs_template_path,
					$db
				);
			}
			catch( Exception $e )
			{
				throw $e;
			}
		}
	}
}


function extract_page_containers( $contents )
{
	/* This function extracts page containers from a string '$contents'.
	On success, an array with the page containers (strings) is returned.
	In case of an error, 'FALSE' is returned.

	Parameters		$contents
					string
					A string that contains the contents of a template.

	Return values	Array or Boolean:
					An array is returned on success.  Otherwise a boolean value
					of FALSE is returned.
	
	*/
	$matches = array();

	if( preg_match_all( REGEX_PAGE_CONTAINER, $contents, $matches ) )
	{
		$matches = $matches[0];
		$matches = array_filter( $matches, function( $element )
		{
			if( strpbrk( $element, "()" ) == FALSE )
			{
				return TRUE;
			}
			return FALSE;
		});
		return $matches;
	}

	return $matches;
}


function extract_function_containers( $contents )
{
	/* This function extracts function containers that are contained in a
	string. 
	On success, these function containers (strings) are returned, contained in
	an array.  FALSE is returned in case of an error.

	Parameters		$contents
					string
					A string that contains the contents of a template.

	Return values	Array or Boolean.
					An array is returned on success, a boolean 'FALSE' on failure.

	*/
	// XXX: Can be improved.
	$matches = array();
	if( preg_match_all( REGEX_FUNCTION_CONTAINER, $contents, $matches ) )
	{
		if( count( $matches[0] ) == 0 )
		{
			return array();
		}
		else
		{
			return $matches[0];
		}
	}
	return $matches;
}


function extract_function_containers2( $contents )
{
	/* This function extracts function containers that are contained in a
	string.
	On success, these function containers (strings) are returned, contained in
	an array.  Otherwise 'FALSE' is returned in case of an error.

	Parameters		$contents
					string
					A string that contains the contents of a template.

	Return values	Array or Boolean.
					An array is returned on success, a boolean 'FALSE' on failure.

	TODO:  Change the name of the function ?

	*/
	$matches = array();
	if( preg_match_all( REGEX_FUNCTION_CONTAINER, $contents, $matches ) )
	{
		return $matches[0];
	}
	return FALSE;
}


function find_and_execute_functions( $HTML )
{
	/* This function replaces all function container definitions inside '$HTML'
	with the return value of each of these functions.

	Parameters		$HTML
					String
					The string contains the HTML code including the function
					containers which will be replaced by this function.

	Return value	String
					This function returns the updated HTML code (string).

	For a detailed understand of how thos function works, please read through
	the body of this function.
	
	*/

	// The names of the function containers will be the keys in an array.
	$function_container_keys = extract_function_containers2( $HTML );

	// Return early if there aren't any function containers.
	if( !is_array( $function_container_keys ) ) return $HTML;

	/* Execute each function (the function container key is the name of the
	function) and store the result as the value of that container key. */
	$function_container_values = array();
	foreach( $function_container_keys as $function_container_key )
	{
		// The name of the function to call without parenthesis and parameters.
		$func_name = substr(
			$function_container_key,
			0,
			strpos(
				$function_container_key,
				"("
			)
		);

		// Optional arguments to this function.
		$args_raw = substr(
			$function_container_key,
			strpos(
				$function_container_key,
				"("
			)
		);
		if( strlen(
			str_replace(
				" ",
				"",
				$args_raw
		) ) == 2 )
		{
			// Call the function without any parameters.
			$function_container_values[] = call_user_func(
				$func_name
			);
		}
		else
		{
			// Call the function with parameters.
			$function_parameters = explode(
				",",
				str_replace(
					array(
						"(",
						")",
						" "
					),
					"",
					$args_raw
				)
			);

			$function_container_values[] = call_user_func_array(
				$func_name,
				$function_parameters
			);
		}
	}

	$HTML = str_replace( $function_container_keys, $function_container_values,
						 $HTML );
	
	return $HTML;
}


function setup_website_project( $project_name )
{
	/* This function sets up a new website project.  On failure, an exception
	is thrown.  On success, TRUE is returned.

	Parameters		$project_name
					string
					The name of the project.

	Return values	TRUE or throws an exception.
					TRUE is returned on success; an exception is thrown on
					failure.

	Read through the body of this function in order to know how it works.

	*/

	// The path to the root directory of the website project.
	$project_root = makepath( ABSPATH_WEBSITE_PROJECTS, $project_name );

	// Try to create the website project directory.
	if( !mkdir( $project_root, 0777, TRUE ) )
	{
		// The website project could not be created.
		throw new Exception(
			sprintf(
				ERROR_CREATE_DIRECTORY,
				$project_name
			)
		);
	}

	// Create the required directories and throw an exception if an error occurs.
	$website_project_required_directories = array(
		RELPATH_BACKUPS,
		RELPATH_DATABASE,
		RELPATH_MODULES,
		RELPATH_TEMPLATES,
		RELPATH_WEBSITE,
		RELPATH_WEBSITE_CSS,
		RELPATH_WEBSITE_DOWNLOADS,
		RELPATH_WEBSITE_JS,
		RELPATH_WEBSITE_MEDIA,
		RELPATH_WEBSITE_PAGES
	);
	foreach( $website_project_required_directories as $dir )
	{
		if( !mkdir( makepath( $project_root, $dir ), 0777, TRUE ) )
		{
			// A required website project directory could not be created.
			throw new Exception(
				sprintf(
					ERROR_CREATE_DIRECTORY,
					$dir
				)
			);
		}
	}

	// Try to create the database.  Throw an exception on failure.
	try
	{
		$sqlite3_db = new SQLite3(
			makepath(
				$project_root,
				RELPATH_DATABASE,
				DB_NAME
			)
		);
	}
	catch( Exception $e )
	{
		throw $e;
	}


	/* Create the required database tables.  Throw an exception is a table could
	not be created. */
	foreach(
		array(
			DB_CREATE_TABLE_PAGE,
			DB_CREATE_TABLE_CONTAINER
		)
		as $sql_stmt
	)
	{
		if( !$sqlite3_db->exec( $sql_stmt ) )
		{
			// A table could not be created.  Throw an Exception.
			throw new Exception( ERROR_CREATE_SQLITE_TABLE );
		}
	}
	$sqlite3_db->close();

	// The website project has now been successfully set up.
	return TRUE;
	
}


function validate_website_project( $project_name, &$error_message )
{
	/* This function will be called after the user has decided to open an existing
	website project.  This function makes sure, that the website project is still
	complete in case of required directories and files.  If it is not complete,
	FALSE is returned.  Otherwise, TRUE is returned.
	The error message is set by reference.

	Parameters		$project_name
					string
					The name of the website project.

					&$error_message
					string
					An empty variable that will later contain an error message
					if an error has occured.

	Return values	TRUE/FALSE or an exception is thrown.
					TRUE is returned on success; FALSE on an error.

	*/
	// The website project directory has to exist.
	if( !file_exists( makepath( ABSPATH_WEBSITE_PROJECTS, $project_name ) ) )
	{
		/* The website project directory does not exist.  Very unlikely, because
		the entire website project directory is scanned before all available
		website projects are shown in the "open website project dialog". */
		$error_message = sprintf( ERROR_DIRECTORY_DOES_NOT_EXIST, $project_name );
		return FALSE;
	}

	// Check whether all required directories exist.
	foreach(
		array(
			RELPATH_BACKUPS,
			RELPATH_DATABASE,
			RELPATH_MODULES,
			RELPATH_TEMPLATES,
			RELPATH_WEBSITE,
			RELPATH_WEBSITE_CSS,
			RELPATH_WEBSITE_DOWNLOADS,
			RELPATH_WEBSITE_JS,
			RELPATH_WEBSITE_MEDIA,
			RELPATH_WEBSITE_PAGES	
		)
		as $dir
	)
	{
		if( !file_exists(
			makepath(
				ABSPATH_WEBSITE_PROJECTS,
				$project_name,
				$dir
			)
		))
		{
			// Error:  A required website project directory does not exist.
			$error_message = sprintf( ERROR_DIRECTORY_DOES_NOT_EXIST, $dir );
			return FALSE;
		}
	}
	
	// Check if the database exists.
	if( !file_exists(
		makepath(
			ABSPATH_WEBSITE_PROJECTS,
			$project_name,
			RELPATH_DATABASE,
			DB_NAME
		)
	))
	{
		// Error:  The database does not exist.
		$error_message = sprintf( ERROR_DATABASE_DOES_NOT_EXIST, DB_NAME );
		return FALSE;
	}
		
	
	// Check if the tables inside the database exist.
	try
	{
		$sqlite3db = new SQLite3(
			makepath(
				ABSPATH_WEBSITE_PROJECTS,
				$project_name,
				RELPATH_DATABASE,
				DB_NAME
			)
		);
	}
	// FIXME:  Why throw an exception all of the sudden ? - Nah, it is valid here.
	catch( Exception $e )
	{
		throw $e;
	}


	foreach(
		array(
			"page",
			"container"
		)
		as $tbl_name
	)
	{
		$sqlite_result = $sqlite3db->query(
			sprintf(
				"SELECT name FROM sqlite_master
				WHERE type = 'table' AND
				name = '%s'",
				$tbl_name
			)
		);
		if( $sqlite_result->fetchArray() == FALSE )
		{
			// Error:  A required sqlite table does not exist.
			$error_message = sprintf(
				ERROR_DATABASE_TABLE_DOES_NOT_EXIST,
				$tbl_name
			);
			return FALSE;
		}
		$sqlite_result->finalize();
	}
	$sqlite3db->close();

	/* The project directory has been validated successfully.  The project
	can now be opened. */
	return TRUE;
}


function dill_utils_website_structure_get_siblings( $db, $parent_id )
{
	/* Returns information of the siblings of the selected item of the website
	structure.

	Parameters		$db
					SQLite3
					Represents an established connection to an SQLite3 database.

					$parent_id
					integer
					The 'id' (database value) of the parent of the selected item.
					If NULL, then parent items are returned.

	Return values	Array or FALSE
					An array that consists of arrays.  Each of these arrays
					contains information about a sibling.
					FALSE is returned in case of an error.  Actually, the
					function "db_get_parent_pages" returns FALSE on failure.

	*/
	if( $parent_id == NULL )
	{
		return db_get_parent_pages( $db );
	}
	else
	{
		return db_get_child_pages( $db, $parent_id );
	}
	
}


function dill_utils_website_structure_get_data_for_selected_item( $current_website_structure,
	$wxtreectrl, $selected_item )
{
	/* This function returns database data as an array for the selected item
	in a wxTreeCtrl widget.

	Parameters		$current_website_structure
					Multidimensional array
					Represents the current website structure.

					$wxtreectrl
					wxTreeCtrl widget.
					The selected item belongs to this widget.

					$selected_item
					string
					The name of the selected item.

	Return values	Array or FALSE
					The array contains database data for that selected item.
					FALSE is returned in case of an error.

	*/
	if( $current_website_structure == FALSE )
	{
		return FALSE;
	}

	/* Now we need to store the names of the pages from the lowest
	child up to the root element. These names are stored in an array,
	where the first element is the name of the parent element (not the
	root element). */
	$child_to_root_names = array(
		$selected_item
	);
	$wxtreeitemid = $wxtreectrl->GetSelection();
	while( TRUE )
	{
		$parent_wxtreeitemid = $wxtreectrl->GetItemParent(
			$wxtreeitemid
		);
		if( !( $wxtreectrl->GetItemParent( $parent_wxtreeitemid )->IsOk() ) )
		{
			break;
		}
		else
		{
			if( $parent_wxtreeitemid->IsOk() )
			{
				$test =	$wxtreectrl->GetItemText(
					$parent_wxtreeitemid
				);
				array_unshift(
					$child_to_root_names,
					$test
				);
				$wxtreeitemid = $parent_wxtreeitemid;
			}
			else
			{
				break;
			}					
		}
	}

	// Now let's get the 'id' (the database id) of the selected item.
	$current_website_structure_copy = $current_website_structure;
	for( $x = 0; $x < count( $child_to_root_names ); $x++ )
	{
		if( $x == ( count( $child_to_root_names ) - 1 ) )
		{
			break;
		}
		if( count( $current_website_structure_copy[$child_to_root_names[$x]]["children"] ) > 0 )
		{
			$current_website_structure_copy = $current_website_structure_copy[$child_to_root_names[$x]];
			$current_website_structure_copy = $current_website_structure_copy["children"];
		}
	}

	$selected_item_data = $current_website_structure_copy[array_pop( $child_to_root_names )]["self"];

	return $selected_item_data;
}


function make_cws( $website_structure_representation )
{
	/* Builds an array that represents a simplfied website structure
	representation.

	Parameters		$website_structure_representation
					array
					An array that represents a more complex website structure
					representation.

	Return values	Array
					The array represents a simple website structure representation.

	Note:  Recursive function.
	*/
	$arr = array();
	foreach( $website_structure_representation as &$item )
	{
		$arr[$item["self"]->get( "name" )] = array(
			"self" => $item["self"]->get( "name" ),
      "id" => $item["self"]->get( "id" )
		);

		$arr[$item["self"]->get( "name" )]["children"] = make_cws( $item["children"] );
	}
	return $arr;
}

?>
